﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Reflection;
using System.Collections;

namespace LoadGenerator
{
    class UserTypeRegistry
    {
        static UserTypeRegistry REGISTER = new UserTypeRegistry();

        Hashtable _usertypes;

        public static UserTypeRegistry instance { get { return REGISTER; } }

        private UserTypeRegistry()
        {
            _usertypes = new Hashtable();
            _usertypes.Add("Normal", typeof(TestUser));
            _usertypes.Add("Reader", typeof(ReaderTestUser));
        }

        public bool registeredType(String type)
        {
            return _usertypes.ContainsKey(type);
        }

        public String registeredTypes()
        {
            String val = "";
            foreach (String key in _usertypes.Keys)
                val += "," + key;
            return val;
        }

        public User create(String type, SvrConf conf, object o, int no)
        {
            User ret = null;
            if (_usertypes.ContainsKey(type))
            {
                Type t = (Type)_usertypes[type];
                ConstructorInfo info = t.GetConstructor(new Type[] { conf.GetType(), o.GetType(), no.GetType() });
                if (info != null)
                {
                    ret = (User)info.Invoke(new object[] { conf, o, no });
                }
            }

            return ret;
        }
    }

    abstract class User
    {
        protected SvrConf _config;
        protected ClientConnection _connection;
        protected object _waittostart;
        protected int _userno;

        public User(SvrConf conf, object o, int no)
        {
            _config = conf;
            _waittostart = o;
            _userno = no;
        }

        public virtual void runUser()
        {
            lock (_waittostart)
            {
                Monitor.Wait(_waittostart);
            }

            try
            {
                _connection = new ClientConnection(_config);
                sendreceive(_connection);
            }
            finally
            {
                if (_connection != null)
                    _connection.Dispose();
            }
        }

        protected abstract void sendreceive(ClientConnection conn);
    }

    class ReaderTestUser : User
    {
        public ReaderTestUser(SvrConf conf, object o, int userno) : base(conf, o, userno)
        {
        }

        protected override void sendreceive(ClientConnection conn)
        {
            ByteData data = new ByteData();
            int cnt = 0; //send data for 10 times before disconnecting. TODO:
            while (cnt < 10)
            {
                DateTime tm = DateTime.Now;
                String send = "TESTING " + cnt;
                System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
                data.data = enc.GetBytes(send);
                int sent = conn.sendData(data);

                Thread.Sleep(10);//send after a delay
                cnt++;
                send = "TESTING " + cnt;
                data.data = enc.GetBytes(send);

                sent += conn.sendData(data);

                conn.receiveData(data, ((sent + 5) * 2) + 5); //read back
                DateTime end = DateTime.Now;
                Console.WriteLine("Duration taken: " + end.Subtract(tm).TotalMilliseconds);
                cnt++;
            }
        }
    }

    class TestUser : User
    {
        public TestUser(SvrConf conf, object o, int userno) : base(conf, o, userno)
        {
        }

        protected override void sendreceive(ClientConnection conn)
        {
            ByteData data = new ByteData();
            int cnt = 0; //send data for 10 times before disconnecting. TODO:
            while (cnt < 10)
            {
                String send = "TESTING " + cnt;
                System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
                data.data = enc.GetBytes(send);

                DateTime tm = DateTime.Now;
                int sent = conn.sendData(data);
                conn.receiveData(data, ((sent + 5) * 2) + 5); //read back
                DateTime end = DateTime.Now;
                Console.WriteLine("Duration taken: " + end.Subtract(tm).TotalMilliseconds);
                cnt++;
            }
        }
    }

    class HTTPUser : User
    {
        public HTTPUser(SvrConf conf, object o, int userno) : base(conf, o, userno)
        {
        }

         //TODO: Implement a HTTP User who can post and receive data
        protected override void sendreceive(ClientConnection conn)
        {
        }
    }
}
